//
//  ViewController.m
//  js交互
//
//  Created by MyMac on 16/7/22.
//  Copyright © 2016年 tongfang. All rights reserved.
//

#import "ViewController.h"
#import <JavaScriptCore/JavaScriptCore.h>
@interface ViewController ()<UIWebViewDelegate>
@property (weak, nonatomic) IBOutlet UIWebView *webView;
@property(nonatomic,strong)NSURL*URL;
@property (strong, nonatomic) JSContext*jsContext;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.webView.delegate=self;
    NSURL*URL=[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"demo" ofType:@"html"]];
    self.URL=URL;
    [self.webView loadRequest:[NSURLRequest  requestWithURL:URL]];
//    JSVirtualMachine为JavaScript的运行提供了底层资源
//    JSContext就为其提供着运行环境，通过- (JSValue *)evaluateScript:(NSString *)script;方法就可以执行一段JavaScript脚本.并且如果其中有方法、变量等信息都会被存储在其中以便在需要的时候使用.而JSContext的创建都是基于JSVirtualMachine：- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine;，如果是使用- (id)init;进行初始化，那么在其内部会自动创建一个新的JSVirtualMachine对象然后调用前边的初始化方法。
//JSValue则可以说是JavaScript和Object-C之间互换的桥梁，它提供了多种方法可以方便地把JavaScript数据类型转换成Objective-C，或者是转换过去
//   self.jsContext = [webView valueForKeyPath :@"documentView.webView.mainFrame.javaScriptContext"];
    

}
-(IBAction)addCalculate:(id)sender{
    //创建一个执行上下文，内部会自己创建一个JSVirtualMachine
    JSContext*context=[[JSContext alloc]init];
    //执行一段js，获得一个js的结果
    JSValue*resultValue = [context evaluateScript:@"27+8"];
    NSLog(@"%@",resultValue);
    //将一个得到的js类型转换成oc类型
    int ocResult=[resultValue toInt32];
    NSLog(@"%d",ocResult);

}
//JSValue是js的变量，可以动过数组或字典的方式获得对应的值
-(IBAction)changeJS:(id)sender{
   JSContext*context=[[JSContext alloc]init];
    //js的容错我们可以用这个异常处理函数获取
   [context setExceptionHandler:^(JSContext *con, JSValue *va) {
       NSLog(@"错误%@",va);
       con.exception=va;
   }];
    //JSContext对执行的js的变量或方法有存储作用,所以我们可以执行多段js
   [context evaluateScript:@"var arr=['罗杰',11,33];"];
   [context evaluateScript:@"var name='我是测试demo';"];
   [context evaluateScript:@"var dic={\"name\":\"罗杰\",\"生日\":\"1111\"}"];
    JSValue*jsArr=[context objectForKeyedSubscript:@"arr"];
    JSValue*jsName=context[@"name"];
    JSValue*jsDict=context[@"dic"];
    //修改js的变量
    jsArr[0]=@"luojie";
    //JSValue遵循了js的特性，不越界，自动延展
    jsArr[10]=@"我是第10个元素";
    [jsArr setObject:@99 forKeyedSubscript:@"2"];
    NSString*ocName=[jsName toString];
    NSArray*ocArr=[jsArr toArray];
    NSDictionary*ocDict=[jsDict toDictionary];
    NSLog(@"ocArr:%@\r\nocName:%@\r\nocDict:%@",ocArr,ocName,ocDict);
}
//JSContext是执行js的环境，通过数组或字典的方式可以设置js方法或变量
//js调用本地方法
-(IBAction)jsCallNativeFunction:(id)sender{
    JSContext*context=[[JSContext alloc]init];
    [context setExceptionHandler:^(JSContext *con, JSValue *va) {
        NSLog(@"错误%@",va);
        con.exception=va;
    }];
    //给js赋值一个log方法
    //在Block内都不要直接使用其外部定义的JSContext对象或者JSValue，应该将其当做参数传入到Block中，或者通过JSContext的类方法+ (JSContext *)currentContext;来获得。否则会造成循环引用使得内存无法被正确释放,应为持有block的也是这个context
    context[@"log"]=^(NSString*p1,NSString*p2){
        NSLog(@"我要开始登陆啦!\r\np1:%@  p2:%@",p1,p2);
        //js当前调用这个方法的对象
        JSValue*sender=[JSContext currentThis];
        NSLog(@"调用者:%@",sender);
        //js传参是不确定的，可以这样拿到参数列表
        NSLog(@"参数列表:%@",[JSContext currentArguments]);
    };
    void(^reg)(void)=^{
        NSLog(@"我要注销啦!\r\n");
    };
    //给js赋值一个register方法
    [context setObject:reg forKeyedSubscript:@"register"];
    //通过context，调用js的log和register方法
#warning 通过在本地注册一个方法，再通过js调用这个方法，就可以实现js与本地进行交互啦
    [context evaluateScript:@"log('我是账号:admin','我是密码:123456','334sdfsdfsd');"];
    [context evaluateScript:@"register()"];
}

-(IBAction)nativeCallJSFunction:(id)sender{
    JSContext*context=[[JSContext alloc]init];
    [context setExceptionHandler:^(JSContext *con, JSValue *va) {
        NSLog(@"错误%@",va);
        con.exception=va;
    }];
//JSValue还提供- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments;让我们可以直接简单地调用对象上的方法。只是如果定义的方法是全局函数，那么很显然应该在JSContext的globalObject对象上调用该方法；如果是某JavaScript对象上的方法，就应该用相应的JSValue对象调用
    [context evaluateScript:@"function add(a,b) {return a+b;}"];
    //获得js的add方法
    JSValue*add=[context objectForKeyedSubscript:@"add"];
   JSValue*addResult = [add callWithArguments:@[@11,@55]];
    NSLog(@"addResult:%d",[addResult toInt32]);
}

- (IBAction)nativeAlert:(id)sender {
    
        [self.jsContext evaluateScript:@"jsAlert('回调js方法')" withSourceURL:self.URL];
    
}
- (void)demoHtmlAlertFunction {
    [self.jsContext setExceptionHandler:^(JSContext *con, JSValue *va) {
        NSLog(@"错误%@",va);
        con.exception=va;
    }];
    
    self.jsContext[@"ocAlert"]=^(){
        //Outside of a callback from JavaScript this method will return nil,所以currentArguments在回调外的线程将返回nil
        NSString*p1=[[JSContext  currentArguments][0]toString];
        NSString*p2=[[JSContext  currentArguments][1]toString];
        dispatch_async(dispatch_get_main_queue(), ^{
            UIAlertView*alertView=[[UIAlertView alloc]initWithTitle:@"js调用本地方法" message:nil delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:p1,p2,nil];
            [alertView show];
            
        });
        
    };}

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    // 通过UIWebView获得网页中的JavaScript执行环境,以此和webView取得联系
   self.jsContext = [webView valueForKeyPath :@"documentView.webView.mainFrame.javaScriptContext"];
  [self demoHtmlAlertFunction];
    });


    return YES;
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end
